﻿using System.Diagnostics;
using System.Drawing;

namespace KingerRobot.MexicoSailunMDC.Biz;
/// <summary>
/// 堆垛机任务执行(出库线体和堆垛机分离任务)
/// 业务-区域-堆垛机01
/// </summary>
public class WarehousingBiz : K8.MDCBB.Biz
{
    #region 配置参数
    private ParamData Param { get; set; }

    private string[] Code = new string[]
    {
        "F00-00：X轴极限正报警",
        "F00-01：X轴极限负报警",
        "F00-02：Z轴极限正报警",
        "F00-03：Z轴极限负报警",
        "F00-04：T1轴极限正报警",
        "F00-05：T1轴极限负报警",
        "F00-06：T2轴极限正报警",
        "F00-07：T2轴极限负报警",
        "F00-08：入库口线体没有安全信号",
        "F00-09：出库口线体没有安全信号",
        "F00-10：X轴故障",
        "F00-11：Z轴故障",
        "F00-12：T1轴故障",
        "F00-13：T2轴故障",
        "F00-14：载物台超速检测报警",
        "F00-15：输入指令错误",
        "F01-00：平移激光测距无数据",
        "F01-01：提升激光测距无数据",
        "F01-02：X轴超扭矩",
        "F01-03：Z轴超扭矩",
        "F01-04：T1轴超扭矩",
        "F01-05：T2轴超扭矩",
        "F01-06：X轴极限开关异常",
        "F01-07：Z轴极限开关异常",
        "F01-08：T1轴极限开关异常",
        "F01-09：T2轴极限开关异常",
        "F01-10：X轴定位目标超限",
        "F01-11：Z轴定位目标超限",
        "F01-12：T1轴定位目标超限",
        "F01-13：T2轴定位目标超限",
        "F01-14：钢丝绳断裂检测1",
        "F01-15：钢丝绳断裂检测2",
        "F02-00：T1轴货叉未到安全位",
        "F02-01：T2轴货叉未到安全位",
        "F02-02：X轴未准备好启动",
        "F02-03：Z轴未准备好启动",
        "F02-04：T1轴未准备好启动",
        "F02-05：T2轴未准备好启动",
        "F02-06：货叉进入禁止区域",
        "F02-07：提升激光测距报警",
        "F02-08：平移激光测距报警",
        "F02-09：载物台1物料偏左",
        "F02-10：载物台1物料偏右",
        "F02-11：载物台2物料偏左",
        "F02-12：载物台2物料偏右",
        "F02-13：库满，需人工处理！",
        "F02-14：移动光通讯报警",
        "F02-15：固定光通讯报警",
        "F03-00：线体现场急停",
        "F03-01：上位机指令重复",
        "F03-02：上位机指令超限",
        "F03-03：远端通讯急停",
        "F03-04：电箱急停",
        "F03-05：上位机通讯报警",
        "F03-06：上位机指令错误",
        "F03-07：T1取料前方无货",
        "F03-08：T1放料前方有货",
        "F03-09：T2取料前方无货",
        "F03-10：T2放料前方有货",
        "F03-11：初始化后载物台1有箱报警",
        "F03-12：初始化后载物台2有箱报警",
        "F03-13：T1放完成，载物台1有货",
        "F03-14：T2放完成，载物台2有货",
        "F03-15：报警备用",
        "F04-00：延时未到位退出自动",
        "F04-01：异常口小车没有到位",
        "F04-02：前安全门报警",
        "F04-03：后安全门报警",
        "F04-04：运动控制器通讯故障",
        "F04-05：运动控制器报警",
        "F04-06：平移X轴未归零",
        "F04-07：提升Z轴未归零",
        "F04-08：货叉T1轴未归零",
        "F04-09：货叉T2轴未归零",
        "F04-10：伺服未使能",
        "F04-11：护栏门开报警",
        "F04-12：任务超时未完成报警",
        "F04-13：任务完成，上位机反馈超时",
        "F04-14：报警备用",
        "F04-15：安全钳卡住报警",
        "A01-001：设备状态:手动",

    };


    class ParamData
    {
        /// <summary>
        /// 设备ID
        /// </summary>
        public long EquipId { get; set; }

        /// <summary>
        /// 设备名称
        /// </summary>
        public string EquipName { get; set; }
        ///// <summary>
        ///// 出库放胎位A
        ///// </summary>
        //public long OutLocA { get; set; }
        ///// <summary>
        ///// 出库放胎位
        ///// </summary>
        //public long OutLocB { get; set; }
    }
    #endregion

    #region PLC 通讯参数
    private ValueData Data { get; set; }
    class ValueData
    {
        /// <summary>
        /// A叉任务号(读)
        /// </summary>
        public int rTaskANo { get; set; }
        /// <summary>
        /// A叉任务号(写)
        /// </summary>
        public int taskA_No { get; set; }
        /// <summary>
        /// 有胎标识
        /// </summary>
        public int rStatus_A { get; set; }


        /// <summary>
        /// B叉任务号(读)
        /// </summary>
        public int rTaskBNo { get; set; }
        /// <summary>
        /// B叉任务号(写)
        /// </summary>
        public int taskB_No { get; set; }
        /// <summary>
        /// 有胎标识
        /// </summary>
        public int rStatus_B { get; set; }


        /// <summary>
        /// 是否可以接受指令
        /// </summary>
        public int rEquipCmdFlag { get; set; }


        /// <summary>
        /// 堆垛机状态
        /// </summary>
        public int rEquipStatus { get; set; }
        /// <summary>
        /// 报警状态
        /// </summary>
        public int errorNo { get; set; }
        /// <summary>
        /// 报警信号
        /// </summary>
        public string errorCode { get; set; }

        public string ProductCode_A { get; set; }
        public string ProductCode_B { get; set; }

        /// <summary>
        /// b库上方出库放货位
        /// </summary>
        public int rStatus_C { get; set; }

        public int rStatus_D { get; set; }

        /// <summary>
        /// a位置允许取胎
        /// </summary>
        public int? PickUpA { get; set; }
        /// <summary>
        /// a位置允许取胎
        /// </summary>
        public int? PickUpB { get; set; }
    }
    #endregion

    #region 缓存
    private CacheData Cache { get; set; }
    class CacheData
    {
        /// <summary>
        /// A叉任务号
        /// </summary>
        public int TaskA_No { get; set; }
        /// <summary>
        /// B叉任务号
        /// </summary>
        public int TaskB_No { get; set; }

        /// <summary>
        /// 完成任务
        /// </summary>
        public int Finish_TaskA { get; set; }

        /// <summary>
        /// 完成任务
        /// </summary>
        public int Finish_TaskB { get; set; }

        public List<CrnTaskCache> CrnTaskList = new List<CrnTaskCache>();
    }
    class CrnTaskCache
    {
        long OutLoc { get; set; }
        int TaskNo { get; set; }
    }
    #endregion

    #region 实时日志
    private StepData Step { get; set; }
    class StepData
    {
        private string _msg = string.Empty;
        public string StepMsg
        {
            get
            {
                return this._msg;
            }
            set
            {
                if (string.IsNullOrWhiteSpace(value))
                {
                    this._msg = string.Empty;
                    return;
                }
                this._msg = $"{System.DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}>>{value}";
            }
        }
    }
    private void StepMsg(string msg)
    {
        this.Step.StepMsg = msg;
        this.WriteLog(msg);
    }
    #endregion

    #region 文件日志（运行需要 Nlog 配置）
    private void WriteLog(string message)
    {
        K8.LSF.Client.Log(new K8.LSF.Log { Data = message });
    }
    #endregion

    /// <summary>
    /// 和 PLC 通讯对象
    /// </summary>
    private BizData BizData { get; set; }
    public int Execute(BizData writer)
    {
        // Excute 方法外使用 BizData
        this.BizData = writer;
        this.StepMsg($"堆垛机{Param.EquipId}开始执行");
        // 设备故障
        string errorCode = string.Empty;
        StringBuilder stringBuilder = new StringBuilder();
        var errMsg = string.Empty;
        if (Data.errorNo != 0)
        {
            this.StepMsg($"堆垛机{Param.EquipId}-{Param.EquipName} |故障Value.errorNo{Data.errorNo}");
            try
            {
                StringBuilder str = new StringBuilder();

                // 解析
                errorCode = Data.errorCode;
                var codes = errorCode.Split(',');
                for (int i = 0; i < codes.Length; i++)
                {
                    var codeE = Convert.ToString(int.Parse(codes[i]), 2).PadLeft(8, '0');
                    var codeA = codeE.ToCharArray().Reverse();
                    var codeS = string.Join("", codeA);
                    str.Append(codeS);
                }
                var strArray = str.ToString().ToCharArray();
                for (int i = 0; i < strArray.Length; i++)
                {
                    if (int.Parse(strArray[i].ToString()) == 1)
                    {
                        stringBuilder.Append($"{Code[i]} |");
                    }
                }
                errMsg = stringBuilder.ToString();
            }
            catch
            {
                errMsg = "解析错误信息失败";
            }
        }
        else if (Data.rEquipStatus == (int)EnumStackerStatus.Manual)  //手动状态 添加一条报警信息
        {
            this.StepMsg($"堆垛机{Param.EquipId}-{Param.EquipName} |进入手动状态");
            //Random rd = new Random();
            //var errNo = rd.Next(100000, 999999);
            Data.errorNo = 2220;
            Data.errorCode = "A01-001";
            errorCode = "A01-001";
            errMsg = "设备状态:手动";
        }
        Alarm.Instance.AddAlarmV3(Param.EquipId, Data.errorNo, errorCode, errMsg, Data.taskA_No, Data.taskB_No);





        // 返回参数
        var result = 0;

        var taskService = K8.RSF.Client.Create<ITaskService>();
        var inputService = K8.RSF.Client.Create<IWarehousingService>();
        var outputService = K8.RSF.Client.Create<IWarehousingOutService>();

        // 判断堆垛机是否是自动状态、是否可接受指令
        this.StepMsg($"堆垛机{Param.EquipId}-{Param.EquipName} |" +
             $"设备状态:{GetStackerStatus(Data.rEquipStatus)}|" +
             $"堆垛机可接受指令：{GetStackerAcceptCmd(Data.rEquipCmdFlag)} |" +
             $"A缓存任务{Cache.TaskA_No} | A写任务{Data.taskA_No} | A读任务 {Data.rTaskANo} | " +
             $"B缓存任务{Cache.TaskB_No} | B写任务{Data.taskB_No} | B读任务 {Data.rTaskBNo} |" +
             $"异常编号{Data.errorCode} | {stringBuilder}");

        // 任务完成
        //this.StepMsg($"堆垛机{Param.EquipId}-{Param.EquipName} ");
        TaskABFinishV2(taskService, inputService, outputService);
        //this.StepMsg($"堆垛机{Param.EquipId}-{Param.EquipName} ");

        // 防止重复调用（线体任务号 == 创建成功缓存任务号【正在执行】，线体写入任务号 != 堆垛机反馈完成流水号【未执行完】）
        var executedFlagA = Cache.TaskA_No != 0 && Cache.TaskA_No == Data.taskA_No && Data.taskA_No != Data.rTaskANo;
        var executedFlagB = Cache.TaskB_No != 0 && Cache.TaskB_No == Data.taskB_No && Data.taskB_No != Data.rTaskBNo;
        if (executedFlagA || executedFlagB)
        {
            var tA = executedFlagA ? Cache.TaskA_No : executedFlagB ? Cache.TaskB_No : Cache.TaskA_No;
            var tB = executedFlagB ? Cache.TaskB_No : executedFlagA ? Cache.TaskA_No : Cache.TaskB_No;
            this.StepMsg($"堆垛机任务{tA},{tB}正在执行，等待完成后继续执行.");
            return result;
        }

        // 临时注释 自动，可接受指令
        if (Data.rEquipStatus != (int)EnumStackerStatus.Auto
            || Data.rEquipCmdFlag != (int)EnumStackerCmd.AcceptCmd)
        {
            this.StepMsg($"停止执行，因为设备状态:{GetStackerStatus(Data.rEquipStatus)}," +
                $"堆垛机可接受指令:{GetStackerAcceptCmd(Data.rEquipCmdFlag)}");
            // 非自动状态返回
            return result;
        }

        this.StepMsg($"{Param.EquipId}记录货叉任务");
        // 堆垛机货叉是否有正在执行任务未完成
        var taskMessage = taskService.GetTaskByEquipId(Param.EquipId);
        if (!string.IsNullOrWhiteSpace(taskMessage))
        {
            this.StepMsg(taskMessage);
            // 非自动状态返回
            return result;
        }
        //this.StepMsg($"{Param.EquipId}记录堆垛机所有任务");
        // 获取堆垛机所有任务
        var tasks = taskService.GetAllExecuteTaskByEquipIdV2(Param.EquipId);
        if (tasks.Count == 0)
        {
            this.StepMsg($"{Param.EquipId}堆垛机没有可执行的出入库任务 或 货叉正在执行任务");
            return result;
        }
        //this.StepMsg($"{Param.EquipId}堆垛机开始执行完成,任务{tasks.Select(t => t.TaskNo).ToJson()}");


        ExcuteResult(tasks, inputService, outputService);
        this.StepMsg($"{Param.EquipId}堆垛机执行完成");
        return result;
    }

    public int ExcuteResult(List<BizTaskBean> tasks,
        IWarehousingService inputService,
        IWarehousingOutService outputService)
    {
        try
        {
            this.StepMsg($"执行堆垛机任务号 {tasks.Select(t => t.TaskNo).ToJson()}");
            var positionService = K8.RSF.Client.Create<IPositionService>();
            var crnService = K8.RSF.Client.Create<ICrnService>();
            var taskService = K8.RSF.Client.Create<ITaskService>();

            var taskGroups = tasks.Select(x => x.TaskGroup).Distinct().ToList();

            foreach (var taskGroup in taskGroups)
            {
                // 配对任务
                var exeTasks = tasks.Where(x => x.TaskGroup == taskGroup).ToList();

                // 任务数
                var exeTaskCount = exeTasks.Count();
                this.StepMsg($"执行堆垛机组号{taskGroup}可执行任务数 {exeTaskCount}");
                if (exeTaskCount == 0)
                {
                    this.StepMsg($"组号 {taskGroup} 未找到可执行任务");

                    continue;
                }

                var taskFirst = exeTasks.FirstOrDefault(); // 第一条任务
                var typeFirst = taskFirst.CmdType.GetValueOrDefault(); // 类型

                if (typeFirst == (int)EnumTaskType.Exwarehouse)
                {

                    //验证是否是出库到立库异常库位任务
                    if (taskService.CheckOutTaskType(taskFirst.TargetLoc.GetValueOrDefault()) == 0)
                    {
                        //判断目标位置是否是b库上层 如果是的话则判断rStatus_C/D
                        var targetLoc = taskFirst.TargetLoc ?? 0;
                        this.StepMsg($"出库任务放胎目标位{targetLoc}");
                        var crnOutput = taskService.GetAsrsCrnOutputByOutPutLocId(targetLoc);
                        if (crnOutput is not null && crnOutput.AreaId == 10020301 )
                        {
                            // 出库放胎位有胎
                            if (Data.rStatus_C != 0 || Data.rStatus_D != 0)
                            {
                                this.StepMsg($"C 位置有胎编号：{Data.rStatus_C}，D 位置有胎编号：{Data.rStatus_D}");
                                continue;
                            }
                        }
                        else//否则判断rStatus_A / B
                        {
                            // 出库放胎位有胎
                            if (Data.rStatus_A != 0 || Data.rStatus_B != 0)
                            {
                                this.StepMsg($"A 位置有胎编号：{Data.rStatus_A}，B 位置有胎编号：{Data.rStatus_B}");
                                continue;
                            }
                        }
                    }

                }


                var mark = 0;
                if (exeTaskCount == 1)
                {
                    // 单货叉执行

                    var equipId = taskFirst.StartEquip.GetValueOrDefault();
                    var forkAB = crnService.GetForkAB(equipId);
                    if (taskFirst.CmdType == (int)EnumTaskType.Warehousing)
                    {
                        ////如果入库任务两个位置都有胎 则不执行单叉任务
                        ///可能会导致不下发 此处弃用
                        //if (Value.ProductCode_A != ConstFields.Default && Value.ProductCode_B != ConstFields.Default && (Value.ProductCode_B != Value.ProductCode_A))
                        //{
                        //    this.StepMsg($"A叉：{Value.ProductCode_A}，B叉：{Value.ProductCode_B}同时有胎 不可执行单叉入库任务");
                        //    continue;
                        //}

                        #region 新增 2025-04-13
                        //3号区域读B
                        var pickUp = 0;
                        pickUp = Data.PickUpA ?? 0;
                        if (pickUp == 0)
                        {
                            this.StepMsg($"{equipId}货位信息不允许取货{Data.PickUpA}");

                            continue;
                        }
                        #endregion
                        var startLoc = taskFirst.StartLoc.GetValueOrDefault();
                        forkAB = crnService.GetForkABLoc(equipId, startLoc);
                    }
                    else
                    {
                        var targetLoc = taskFirst.TargetLoc.GetValueOrDefault();
                        forkAB = crnService.GetForkABLoc(equipId, targetLoc);
                    }

                    // 获取取货位信息
                    var positionBean = positionService.GetLocationBean(taskFirst);
                    if (positionBean is null)
                    {
                        this.StepMsg($"{equipId}货位信息不存在");
                        continue;
                    }
                    //this.StepMsg($"taskFirst-{taskFirst.ToJson()},positionBean-{positionBean.ToJson()},forkAB-{forkAB},typeFirst{typeFirst}");

                    mark = this.SingleABTaskV2(inputService, outputService, positionBean, forkAB, typeFirst);
                    if (mark == 0)
                    {
                        //this.StepMsg($"{equipId}，{forkAB}下传失败");
                        continue;
                    }
                }
                else if (exeTaskCount == 2)
                {
                    this.StepMsg($"{Param.EquipId}进入双车");
                    var taskA = new BizTaskBean();
                    var taskB = new BizTaskBean();

                    var positionA = new PositionBean();
                    var positionB = new PositionBean();

                    taskA = taskFirst;
                    //this.StepMsg($"{Param.EquipId}打印 taskFirst{taskFirst.ToJson()}");
                    var equipId = taskA.StartEquip.GetValueOrDefault();
                    //this.StepMsg($"{Param.EquipId}打印 startequip{equipId}");
                    var forkAB = crnService.GetForkAB(equipId);
                    if (taskFirst.CmdType == (int)EnumTaskType.Warehousing)
                    {
                        var startLoc = taskFirst.StartLoc.GetValueOrDefault();
                        forkAB = crnService.GetForkABLoc(equipId, startLoc);
                        #region 新增 2025-04-13
                        //3号区域读B
                        int pickUp = Data.PickUpA ?? 0;
                        if (pickUp == 0)
                        {
                            this.StepMsg($"{equipId}货位信息不允许取货{Data.PickUpA}");
                            continue;
                        } 
                        #endregion
                    }
                    else
                    {
                        var targetLoc = taskFirst.TargetLoc.GetValueOrDefault();
                        forkAB = crnService.GetForkABLoc(equipId, targetLoc);
                    }
                    this.StepMsg($"{Param.EquipId}打印 forkAB{forkAB}");

                    if (forkAB == (int)EnumForkAB.ForkA)
                    {
                        taskB = exeTasks.LastOrDefault();
                    }
                    else if (forkAB == (int)EnumForkAB.ForkB)
                    {
                        taskB = taskFirst;
                        taskA = exeTasks.LastOrDefault();
                    }
                    else
                    {
                        this.StepMsg($"AB叉选择失败");
                        return 0;
                    }
                    //this.StepMsg($"{Param.EquipId}打印 taskA{taskA.ToJson()}");
                    //this.StepMsg($"{Param.EquipId}打印 taskB{taskB.ToJson()}");

                    positionA = positionService.GetLocationBean(taskA);
                    positionB = positionService.GetLocationBean(taskB);
                    //this.StepMsg($"{Param.EquipId}打印 positionA{positionA.ToJson()}");
                    //this.StepMsg($"{Param.EquipId}打印 positionB{positionB.ToJson()}");

                    mark = WriteABForkV2(positionA, positionB);
                    if (mark == 0)
                    {
                        this.StepMsg($"双胎任务下传失败");
                        continue;
                    }

                    // 执行入库、出库
                    if (mark > 0)
                    {
                        mark = SetTaskFlag();
                        //如果是入库任务 则修改入库下发标识
                        if (taskFirst.CmdType == (int)EnumTaskType.Warehousing)
                        {
                            try
                            {
                                setInExcuteFlag();
                            }
                            catch (Exception ex)
                            {
                                this.StepMsg($"修改入库下发标识失败,{ex.ToString()}");
                            }
                        }

                        if (mark > 0)
                        {
                            mark = ExeIOService(typeFirst, taskA.TaskNo.GetValueOrDefault(), inputService, outputService);

                            mark += ExeIOService(typeFirst, taskB.TaskNo.GetValueOrDefault(), inputService, outputService);
                        }

                        SetCacheAB(taskA.TaskNo.GetValueOrDefault(), taskB.TaskNo.GetValueOrDefault());
                    }
                    else
                    {
                        this.StepMsg($"双胎任务执行失败");
                    }
                }

                taskService.SetCrnType(Param.EquipId, typeFirst);

                break;
            }
        }
        catch (Exception ex)
        {
            this.StepMsg($"堆垛机任务执行异常{ex.ToString()}");

        }

        return 1;
    }


    /// <summary>
    /// 修改货叉完成标记
    /// </summary>
    private int SetTaskFlag()
    {
        var mark = this.BizData.WriteData(new
        {
            rWriteMark = 1,
        });
        Thread.Sleep(100);

        return mark;
    }

    private int setInExcuteFlag()
    {
        var mark = this.BizData.WriteData(new
        {
            rExcute = 1,
        });
        Thread.Sleep(100);

        return mark;
    }

    private void ClearCacheA()
    {
        Cache.TaskA_No = 0;

        var stackerBean = new BizStackerBean
        {
            takeA_X = 0,
            takeA_Y = 0,
            takeA_Z = 0,
            takeA_T = 0,
            targetA_X = 0,
            targetA_Y = 0,
            targetA_Z = 0,
            targetA_T = 0,

            n_takeA_X = 0,
            n_takeA_Y = 0,
            n_takeA_Z = 0,
            n_takeA_T = 0,
            n_targetA_X = 0,
            n_targetA_Y = 0,
            n_targetA_Z = 0,
            n_targetA_T = 0,

            taskA_No = 0,
            curFlag = 0,
        };
        // 下发
        this.BizData.WriteData(stackerBean);
    }

    private void ClearCacheB()
    {
        Cache.TaskB_No = 0;

        var stackerBean = new BizStackerBean
        {
            takeB_X = 0,
            takeB_Y = 0,
            takeB_Z = 0,
            takeB_T = 0,
            targetB_X = 0,
            targetB_Y = 0,
            targetB_Z = 0,
            targetB_T = 0,

            n_takeB_X = 0,
            n_takeB_Y = 0,
            n_takeB_Z = 0,
            n_takeB_T = 0,
            n_targetB_X = 0,
            n_targetB_Y = 0,
            n_targetB_Z = 0,
            n_targetB_T = 0,

            taskB_No = 0,
            curFlag = 0,
        };
        // 下发
        this.BizData.WriteData(stackerBean);
    }

    private void SetCacheAB(int taskANo, int taskBNo)
    {
        Cache.TaskA_No = taskANo;
        Cache.TaskB_No = taskBNo;
    }

    private int SingleABTaskV2(
        IWarehousingService inputService,
        IWarehousingOutService outputService,
        PositionBean info,
        int forkAB,
        int type) => forkAB switch
        {
            (int)EnumForkAB.ForkA => SingleATask(info, type, inputService, outputService),
            (int)EnumForkAB.ForkB => SingleBTask(info, type, inputService, outputService),
            _ => 0
        };

    private int SingleATask(PositionBean info,
        int type,
        IWarehousingService inputService,
        IWarehousingOutService outputService)
    {
        var mark = WriteAForkV2(info);
        if (mark == 0)
        {
            return mark;
        }

        if (mark > 0)
        {
            this.StepMsg($"任务A {info.TaskNo},下传PLC成功标识修改");
            mark = SetTaskFlag();

            //如果是入库任务 则修改入库下发标识
            if (type == (int)EnumTaskType.Warehousing)
            {
                try
                {
                    setInExcuteFlag();
                }
                catch (Exception ex)
                {
                    this.StepMsg($"单叉修改入库下发标识失败,{ex.ToString()}");
                }
            }

            if (mark > 0)
            {
                // 执行任务
                mark = ExeIOService(type, info.TaskNo, inputService, outputService);
                Cache.TaskA_No = info.TaskNo;
            }
        }

        return mark;
    }

    private int SingleBTask(PositionBean info,
        int type,
        IWarehousingService inputService,
        IWarehousingOutService outputService)
    {
        var mark = WriteBForkV2(info);
        if (mark == 0)
        {
            return mark;
        }

        if (mark > 0)
        {
            this.StepMsg($"任务B {info.TaskNo},下传PLC成功标识修改");
            mark = SetTaskFlag();

            //如果是入库任务 则修改入库下发标识
            if (type == (int)EnumTaskType.Warehousing)
            {
                try
                {
                    setInExcuteFlag();
                }
                catch (Exception ex)
                {
                    this.StepMsg($"单叉修改入库下发标识失败,{ex.ToString()}");
                }
            }


            if (mark > 0)
            {
                // 执行任务
                mark = ExeIOService(type, info.TaskNo, inputService, outputService);
                Cache.TaskB_No = info.TaskNo;
            }
        }

        return mark;
    }

    private int ExeIOService(int type, int taskNo,
        IWarehousingService inputService,
        IWarehousingOutService outputService)
    {
        int mark = 0;
        if (type == (int)EnumTaskType.Warehousing)
        {
            mark = inputService.ExecuteTask(taskNo, 0);

            if (mark == 0)
            {
                for (int i = 0; i < 3; i++)
                {
                    Thread.Sleep(1000);
                    mark = inputService.ExecuteTask(taskNo, 0);
                    if (mark > 0)
                    {
                        break;
                    }
                }
            }
            this.StepMsg($"入库{type}任务{taskNo},执行{mark}");
        }
        else if (type == (int)EnumTaskType.Exwarehouse)
        {
            this.StepMsg($"出库{type}任务{taskNo},执行");
            mark = outputService.ExecuteTask(taskNo, 0);
        }
        return mark;
    }

    private int WriteAForkV2(PositionBean info)
    {
        // 指令下传
        var stackerBean = new BizStackerBean
        {
            takeA_X = info.TakeX,
            takeA_Y = info.TakeY,
            takeA_Z = info.TakeZ,
            takeA_T = info.TakeT,
            targetA_X = info.PutX,
            targetA_Y = info.PutY,
            targetA_Z = info.PutZ,
            targetA_T = info.PutT,

            n_takeA_X = info.NTakeX,
            n_takeA_Y = info.NTakeY,
            n_takeA_Z = info.NTakeZ,
            n_takeA_T = info.NTakeT,
            n_targetA_X = info.NPutX,
            n_targetA_Y = info.NPutY,
            n_targetA_Z = info.NPutZ,
            n_targetA_T = info.NPutT,

            taskA_No = info.TaskNo,

            takeB_X = 0,
            takeB_Y = 0,
            takeB_Z = 0,
            takeB_T = 0,
            targetB_X = 0,
            targetB_Y = 0,
            targetB_Z = 0,
            targetB_T = 0,

            n_takeB_X = 0,
            n_takeB_Y = 0,
            n_takeB_Z = 0,
            n_takeB_T = 0,
            n_targetB_X = 0,
            n_targetB_Y = 0,
            n_targetB_Z = 0,
            n_targetB_T = 0,

            taskB_No = 0,

            curFlag = 2,
        };

        // 下发
        var mark = this.BizData.WriteData(stackerBean);

        this.StepMsg($"堆垛机A货叉:{Param.EquipId}-{Param.EquipName} |" +
 $"下传指令:{stackerBean.ToJson()}| " +
$"PLC反馈结果:{mark}");

        return mark;
    }

    private int WriteBForkV2(PositionBean info)
    {
        // 指令下传
        var stackerBean = new BizStackerBean
        {
            takeA_X = 0,
            takeA_Y = 0,
            takeA_Z = 0,
            takeA_T = 0,
            targetA_X = 0,
            targetA_Y = 0,
            targetA_Z = 0,
            targetA_T = 0,

            n_takeA_X = 0,
            n_takeA_Y = 0,
            n_takeA_Z = 0,
            n_takeA_T = 0,
            n_targetA_X = 0,
            n_targetA_Y = 0,
            n_targetA_Z = 0,
            n_targetA_T = 0,

            taskA_No = 0,

            takeB_X = info.TakeX,
            takeB_Y = info.TakeY,
            takeB_Z = info.TakeZ,
            takeB_T = info.TakeT,
            targetB_X = info.PutX,
            targetB_Y = info.PutY,
            targetB_Z = info.PutZ,
            targetB_T = info.PutT,

            n_takeB_X = info.NTakeX,
            n_takeB_Y = info.NTakeY,
            n_takeB_Z = info.NTakeZ,
            n_takeB_T = info.NTakeT,
            n_targetB_X = info.NPutX,
            n_targetB_Y = info.NPutY,
            n_targetB_Z = info.NPutZ,
            n_targetB_T = info.NPutT,

            taskB_No = info.TaskNo,
            curFlag = 3,
        };

        // 下发
        var mark = this.BizData.WriteData(stackerBean);

        this.StepMsg($"堆垛机B货叉{Param.EquipId}-{Param.EquipName} |" +
 $"下传指令:{stackerBean.ToJson()}| " +
$"PLC反馈结果:{mark}");

        return mark;
    }

    private int WriteABForkV2(PositionBean positionA,
        PositionBean positionB
        )
    {

        var stackerBean = new BizStackerBean
        {
            //OLDTODO 下传字段值
            takeA_X = positionA.TakeX,
            takeA_Y = positionA.TakeY,
            takeA_Z = positionA.TakeZ,
            takeA_T = positionA.TakeT,
            targetA_X = positionA.PutX,
            targetA_Y = positionA.PutY,
            targetA_Z = positionA.PutZ,
            targetA_T = positionA.PutT,

            n_takeA_X = positionA.NTakeX,
            n_takeA_Y = positionA.NTakeY,
            n_takeA_Z = positionA.NTakeZ,
            n_takeA_T = positionA.NTakeT,
            n_targetA_X = positionA.NPutX,
            n_targetA_Y = positionA.NPutY,
            n_targetA_Z = positionA.NPutZ,
            n_targetA_T = positionA.NPutT,

            taskA_No = positionA.TaskNo,

            takeB_X = positionB.TakeX,
            takeB_Y = positionB.TakeY,
            takeB_Z = positionB.TakeZ,
            takeB_T = positionB.TakeT,
            targetB_X = positionB.PutX,
            targetB_Y = positionB.PutY,
            targetB_Z = positionB.PutZ,
            targetB_T = positionB.PutT,

            n_takeB_X = positionB.NTakeX,
            n_takeB_Y = positionB.NTakeY,
            n_takeB_Z = positionB.NTakeZ,
            n_takeB_T = positionB.NTakeT,
            n_targetB_X = positionB.NPutX,
            n_targetB_Y = positionB.NPutY,
            n_targetB_Z = positionB.NPutZ,
            n_targetB_T = positionB.NPutT,

            taskB_No = positionB.TaskNo,

            curFlag = 1,
        };

        // 下发
        var mark = this.BizData.WriteData(stackerBean);
        this.StepMsg($"双胎 堆垛机{Param.EquipId}-{Param.EquipName} |" +
    $"任务A：{positionA.TaskNo} |" +
    $"任务B：{positionB.TaskNo} |" +
 $"下传指令:{stackerBean.ToJson()}| " +
$"PLC反馈结果:{mark}");
        return mark;
    }


    /// <summary>
    /// 任务完成
    /// </summary>
    /// <param name="taskService"></param>
    /// <param name="inputService"></param>
    /// <param name="outputService"></param>
    private void TaskABFinishV2(ITaskService taskService,
        IWarehousingService inputService,
        IWarehousingOutService outputService)
    {

        StringBuilder stringBuilder = new();

        var task = Task.Run(() =>
        {
            // A号完成
            var dataA = TaskAFinishV2(taskService, stringBuilder, inputService, outputService);
            return dataA;
        });

        var task1 = Task.Run(() =>
        {
            // B号完成
            var dataB = TaskBFinishV2(taskService, stringBuilder, inputService, outputService);
            return dataB;
        });

        var dataA = task.Result;
        var dataB = task1.Result;

        // 出库完成清空点位信息
        OutWriteSwitchV2(dataA.OutN, dataB.OutN);

        // 入库完成清空点位信息
        this.StepMsg($"入库完成同步 MES 中间表参数：{dataA.InN}，{dataB.InN}，{dataA.InTargetLoc}，{dataB.InTargetLoc}");
        InWriteSwitch(dataA.InN, dataB.InN);

        var message = stringBuilder.ToString();
        if (!string.IsNullOrWhiteSpace(message))
        {
            this.StepMsg(message);
        }
    }

    public IOInfo TaskAFinishV2(ITaskService taskService, StringBuilder stringBuilder,
        IWarehousingService inputService,
        IWarehousingOutService outputService)
    {
        IOInfo iOInfo = new();
        // 1、堆垛机反馈任务  
        // 2、放胎位有货
        // 3、指令完成创建的线体任务【堆垛机无反馈、放胎位无货】
        // 4、任务执行完不再执行：Value.rTaskANo != Cache.Finish_TaskA
        if (Data.rTaskANo != 0 && Data.rTaskANo != Cache.Finish_TaskA)
        {

            // 完成信号
            var taskNo = Data.rTaskANo;


            Stopwatch stopwatch = new Stopwatch();
            this.StepMsg($"出库监控到A完成信号,任务号为{taskNo}");
            stopwatch.Start();
            var task = taskService.GetTaskBeanByTaskNoV3(taskNo);
            stopwatch.Stop();
            var milliseconds = stopwatch.ElapsedMilliseconds;
            if (milliseconds >= 1000)
            {
                this.StepMsg($"&&&等待时间超过1s,taskno{taskNo}");
            }
            // this.StepMsg($"A货叉任务{taskNo}完成可执行:task={task?.ToJson()},type = {task?.CmdType}");
            if (task is not null)
            {
                if (task.CmdType == (int)EnumTaskType.Warehousing)
                {
                    iOInfo.InTargetLoc = task.TargetLoc.GetValueOrDefault();
                    iOInfo.InN = InFinish_A(inputService, task, stringBuilder);
                    if (iOInfo.InN > 0)
                    {
                        Cache.Finish_TaskA = Data.rTaskANo;
                    }
                }
                else if (task.CmdType == (int)EnumTaskType.Exwarehouse)
                {
                    iOInfo.OutTaskNo = task.TaskNo.GetValueOrDefault();
                    iOInfo.OutTaskLoc = task.TargetLoc.GetValueOrDefault();
                    this.StepMsg($"出库监控到A完成信号,开始执行任务完成操作,任务号为{taskNo}");
                    (iOInfo.OutN, iOInfo.OutTask) = OutFinish_A_V2(outputService, task, stringBuilder);
                    this.StepMsg($"出库监控到A完成信号,结束任务完成操作,任务号为{taskNo}");
                    if (iOInfo.OutN > 0)
                    {
                        Cache.Finish_TaskA = Data.rTaskANo;
                    }
                }
            }
        }
        return iOInfo;
    }


    public IOInfo TaskBFinishV2(ITaskService taskService, StringBuilder stringBuilder,
        IWarehousingService inputService,
        IWarehousingOutService outputService)
    {
        IOInfo iOInfo = new();
        // 1、堆垛机反馈任务  
        // 2、放胎位有货
        // 3、出库指令完成创建的线体任务【堆垛机无反馈、放胎位无货】
        // 4、任务执行完不再执行：Value.rTaskBNo != Cache.Finish_TaskB
        if (Data.rTaskBNo != 0 && Data.rTaskBNo != Cache.Finish_TaskB)
        {

            var taskNo = Data.rTaskBNo;
            this.StepMsg($"出库监控到B完成信号,任务号为{taskNo}");
            var task = taskService.GetTaskBeanByTaskNoV3(taskNo);
            this.StepMsg($"B货叉任务{taskNo}完成可执行:task={task?.ToJson()},type = {task?.CmdType}");
            //stringBuilder.Append($"B货叉任务{taskNo}完成可执行:task={task?.ToJson()},type = {task?.CmdType}| ");
            if (task is not null)
            {
                if (task.CmdType == (int)EnumTaskType.Warehousing)
                {
                    iOInfo.InTargetLoc = task.TargetLoc.GetValueOrDefault();
                    iOInfo.InN = InFinish_B(inputService, task, stringBuilder);
                    if (iOInfo.InN > 0)
                    {
                        Cache.Finish_TaskB = Data.rTaskBNo;
                    }
                }
                else if (task.CmdType == (int)EnumTaskType.Exwarehouse)
                {
                    iOInfo.OutTaskNo = task.TaskNo.GetValueOrDefault();
                    iOInfo.OutTaskLoc = task.TargetLoc.GetValueOrDefault();
                    this.StepMsg($"出库监控到B完成信号,开始执行任务完成操作,任务号为{taskNo}");
                    (iOInfo.OutN, iOInfo.OutTask) = OutFinish_B_V2(outputService, task, stringBuilder);
                    this.StepMsg($"出库监控到B完成信号,结束任务完成操作,任务号为{taskNo}");
                    if (iOInfo.OutN > 0)
                    {
                        Cache.Finish_TaskB = Data.rTaskBNo;
                    }
                }
            }
        }
        return iOInfo;
    }



    /// <summary>
    /// 入库 A 完成
    /// </summary>
    /// <param name="inputService"></param>
    /// <param name="task"></param>
    /// <param name="stringBuilder"></param>
    private int InFinish_A(IWarehousingService inputService, BizTaskBean task, StringBuilder stringBuilder)
    {
        var result = inputService.FinishTask(task.TaskNo.GetValueOrDefault(), task.TargetLoc.GetValueOrDefault());
        stringBuilder.Append($"A货叉入库任务{Data.rTaskANo}完成：{result} | ");
        return result;
    }

    /// <summary>
    /// 入库 B 完成
    /// </summary>
    /// <param name="inputService"></param>
    /// <param name="task"></param>
    /// <param name="stringBuilder"></param>
    private int InFinish_B(IWarehousingService inputService, BizTaskBean task, StringBuilder stringBuilder)
    {
        var result = inputService.FinishTask(task.TaskNo.GetValueOrDefault(), task.TargetLoc.GetValueOrDefault());
        stringBuilder.Append($"B货叉入库任务{Data.rTaskBNo}完成：{result}  | ");
        return result;
    }


    /// <summary>
    /// 出库 A 完成
    /// </summary>
    /// <param name="outputService"></param>
    /// <param name="task"></param>
    /// <param name="stringBuilder"></param>
    private (int, BizTaskBean?) OutFinish_A_V2(IWarehousingOutService outputService, BizTaskBean task, StringBuilder stringBuilder)
    {

        var bindResult = outputService.FinishTask(task.TaskNo.GetValueOrDefault(), task.TargetLoc.GetValueOrDefault());
        if (bindResult > 0)
        {
            return (1, new());
        }
        return (0, null);
    }

    /// <summary>
    /// 出库 B 完成
    /// </summary>
    /// <param name="outputService"></param>
    /// <param name="task"></param>
    /// <param name="stringBuilder"></param>
    private (int, BizTaskBean?) OutFinish_B_V2(IWarehousingOutService outputService, BizTaskBean task, StringBuilder stringBuilder)
    {
        var bindResult = outputService.FinishTask(task.TaskNo.GetValueOrDefault(), task.TargetLoc.GetValueOrDefault());
        if (bindResult > 0)
        {
            return (1, new());
        }
        return (0, null);
    }

    /// <summary>
    /// 入库完成
    /// </summary>
    /// <param name="outTaskNoA"></param>
    /// <param name="outTaskLocA"></param>
    /// <returns></returns>
    private int ClearCacheATaskFlag()
    {
        this.ClearCacheA();
        return 1;
    }
    private int ClearCacheBTaskFlag()
    {
        this.ClearCacheB();
        return 1;
    }
    private int ClearCacheABTaskFlag()
    {
        this.ClearCacheA();
        this.ClearCacheB();
        return 1;
    }
    private int OutWriteSwitchV2(int outA, int outB) => (outA, outB) switch
    {
        (1, 1) => ClearCacheABTaskFlag(),
        (1, 0) => ClearCacheABTaskFlag(),
        (0, 1) => ClearCacheABTaskFlag(),
        _ => 0,
    };
    private int InWriteSwitch(int inA, int inB) => (inA, inB) switch
    {
        (1, 1) => ClearCacheABTaskFlag(),//| InWarehouse(inTargetLocA) | InWarehouse(inTargetLocB)
        (1, 0) => ClearCacheABTaskFlag(), //  | InWarehouse(inTargetLocA)
        (0, 1) => ClearCacheABTaskFlag(), // | InWarehouse(inTargetLocB)
        _ => 0,
    };


    public class IOInfo
    {
        public long InTargetLoc { get; set; }
        public int InN { get; set; }
        public int OutTaskNo { get; set; }
        public long OutTaskLoc { get; set; }
        public BizTaskBean OutTask { get; set; }
        public int OutN { get; set; }
    }
}
